/*
 * Unidata Platform Community Edition
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 * This file is part of the Unidata Platform Community Edition software.
 *
 * Unidata Platform Community Edition is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Unidata Platform Community Edition is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package org.unidata.mdm.dq.core.serialization;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.unidata.mdm.dq.core.exception.DataQualityExceptionIds;
import org.unidata.mdm.dq.core.exception.DataQualityRuntimeException;
import org.unidata.mdm.dq.core.type.io.DataQualityError;
import org.unidata.mdm.dq.core.type.model.source.DataQualityModel;
import org.unidata.mdm.dq.core.type.rule.SeverityIndicator;
import org.unidata.mdm.system.serialization.xml.XmlObjectSerializer;
import org.unidata.mdm.system.util.CompressUtils;
import org.unidata.mdm.system.util.JsonUtils;

import com.fasterxml.jackson.core.type.TypeReference;

/**
 * @author Mikhail Mikhailov on Oct 14, 2020
 */
public final class DataQualitySerializer {
    /**
     * DQE to string TR.
     */
    private static final TypeReference<Map<String, String>> QUALITY_ERROR_TYPE_REFERENCE
        = new TypeReference<Map<String, String>>(){};
    /**
     * DQE field: functionName.
     */
    private static final String DQE_FIELD_FUNCTION_NAME = "functionName";
    /**
     * DQE field: message.
     */
    private static final String DQE_FIELD_MESSAGE = "message";
    /**
     * DQE field: severity.
     */
    private static final String DQE_FIELD_SEVERITY = "severity";
    /**
     * DQE field: score.
     */
    private static final String DQE_FIELD_SCORE = "score";
    /**
     * DQE field: category.
     */
    private static final String DQE_FIELD_CATEGORY = "category";
    /**
     * Data quality model zip entry name.
     */
    public static final String DATA_QUALITY_MODEL_ZIP_ENTRY = "data-quality-model";
    /**
     * Constructor.
     */
    private DataQualitySerializer() {
        super();
    }
    /**
     * Does uncompress default ZIP compressed data model XML representation.
     * @param data the compressed data
     * @return model
     */
    public static DataQualityModel modelFromCompressedXml(byte[] data) {

        byte[] uncompressed;
        try {
            uncompressed = CompressUtils.unzip(data, DATA_QUALITY_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new DataQualityRuntimeException("Cannot uncompress data quality model.", e,
                    DataQualityExceptionIds.EX_DQ_CANNOT_UNCOMPRESS_DATA);
        }

        return XmlObjectSerializer
                .getInstance()
                .fromXmlString(DataQualityModel.class, new String(uncompressed, StandardCharsets.UTF_8));
    }
    /**
     * Does compress data model to default ZIP compressed XML representation.
     * @param model the model
     * @return bytes
     */
    public static byte[] modelToCompressedXml(DataQualityModel model) {

        String content = XmlObjectSerializer.getInstance().toXmlString(model, false);
        byte[] compressed;
        try {
            compressed = CompressUtils.zip(content.getBytes(StandardCharsets.UTF_8), DATA_QUALITY_MODEL_ZIP_ENTRY);
        } catch (IOException e) {
            throw new DataQualityRuntimeException("Cannot compress data model.", e,
                    DataQualityExceptionIds.EX_DQ_CANNOT_COMPRESS_DATA);
        }

        return compressed;
    }

    public static DataQualityError errorFromString(String s) {

        if (StringUtils.isBlank(s)) {
            return null;
        }

        Map<String, String> errorAsMap = JsonUtils.read(s, QUALITY_ERROR_TYPE_REFERENCE);
        return DataQualityError.builder()
                .functionName(errorAsMap.get(DQE_FIELD_FUNCTION_NAME))
                .message(errorAsMap.get(DQE_FIELD_MESSAGE))
                .severity(SeverityIndicator.fromValue(errorAsMap.get(DQE_FIELD_SEVERITY)))
                .score(NumberUtils.toInt(errorAsMap.get(DQE_FIELD_SCORE), 0))
                .category(errorAsMap.get(DQE_FIELD_CATEGORY))
                .build();
    }

    public static String errorToString(DataQualityError e) {

        if (Objects.isNull(e)) {
            return null;
        }

        return JsonUtils.write(
            Map.of(
                DQE_FIELD_FUNCTION_NAME, e.getFunctionName(),
                DQE_FIELD_MESSAGE, e.getMessage(),
                DQE_FIELD_SEVERITY, e.getSeverity() != null ? e.getSeverity().name() : null,
                DQE_FIELD_SCORE, Integer.toString(e.getScore()),
                DQE_FIELD_CATEGORY, e.getCategory()));
    }
}
